#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <sys/time.h>
#include <sys/stat.h>
#include "http_server.h"
#include "dnscache.h"
#include "log.h"

#define CONFIG_FILE     "WebServer.conf"
#define MIME_TYPES_FILE "mime.conf"

static void print_usage (const char *program);
static void stop (int nsignal);
static void handle_alarm (int nsignal);
static void handle_sigusr1 (int nsignal);
static void handle_sighup (int nsignal);

http_server_t http_server;
configuration_t conf;
configuration_t mime_types;
dnscache_t dnscache;

const char *config_file;
const char *mime_types_file;

int main (int argc, char **argv)
{
	struct sigaction act;
	struct itimerval interval;
	int daemonize;
	pid_t child_pid;
	int ret;
	int i;

	/* Process command-line parameters. */
	config_file = NULL;
	mime_types_file = NULL;
	daemonize = 1;

	i = 1;
	while (i < argc) {
		if (strcasecmp (argv[i], "--no-daemonize") == 0) {
			daemonize = 0;

			i++;
		} else if (strcasecmp (argv[i], "--conf") == 0) {
			if (i == argc - 1) {
				print_usage (argv[0]);
				return -1;
			}

			config_file = argv[i + 1];

			i += 2;
		} else if (strcasecmp (argv[i], "--mime") == 0) {
			if (i == argc - 1) {
				print_usage (argv[0]);
				return -1;
			}

			mime_types_file = argv[i + 1];

			i += 2;
		} else {
			print_usage (argv[0]);
			return -1;
		}
	}

	/* Initialize configuration. */
	configuration_init (&conf, 1);
	configuration_init (&mime_types, 1);

	dnscache_init (&dnscache);

	/* Load configuration. */
	if (!config_file) {
		config_file = CONFIG_FILE;
	}

	if (configuration_load (&conf, config_file) < 0) {
		configuration_free (&conf);

		fprintf (stderr, "Couldn't load config file %s.\n", CONFIG_FILE);
		return -1;
	}

	/* Load MIME types. */
	if (!mime_types_file) {
		mime_types_file = MIME_TYPES_FILE;
	}

	if (configuration_load (&mime_types, mime_types_file) < 0) {
		configuration_free (&mime_types);
		configuration_free (&conf);

		fprintf (stderr, "Couldn't load MIME types file %s.\n", MIME_TYPES_FILE);
		return -1;
	}

	tzset ();

	/* Load HTTP server configuration. */
	if (http_server_load_configuration (&http_server, &conf) < 0) {
		http_server_delete (&http_server);
		configuration_free (&mime_types);
		configuration_free (&conf);

		fprintf (stderr, "Couldn't load HTTP server configuration.\n");
		return -1;
	}

	/* Create HTTP server. */
	if (http_server_create (&http_server) < 0) {
		http_server_delete (&http_server);
		configuration_free (&mime_types);
		configuration_free (&conf);

		fprintf (stderr, "Couldn't create HTTP server.\n");
		return -1;
	}

	if (daemonize) {
		if ((child_pid = fork ()) < 0) {
			perror ("fork");

			http_server_delete (&http_server);

			configuration_free (&mime_types);
			configuration_free (&conf);

			return -1;
		} else if (child_pid != 0) {
			/* Parent process. */
			http_server_delete (&http_server);

			configuration_free (&mime_types);
			configuration_free (&conf);

			return 0;
		}

		if (setsid () < 0) {
			perror ("setsid");

			http_server_delete (&http_server);

			configuration_free (&mime_types);
			configuration_free (&conf);

			return -1;
		}

		/* Fork again. */
		if ((child_pid = fork ()) < 0) {
			http_server_delete (&http_server);

			configuration_free (&mime_types);
			configuration_free (&conf);

			return -1;
		} else if (child_pid != 0) {
			/* Parent process. */
			http_server_delete (&http_server);

			configuration_free (&mime_types);
			configuration_free (&conf);

			return 0;
		}

		if (chdir ("/") < 0) {
			http_server_delete (&http_server);

			configuration_free (&mime_types);
			configuration_free (&conf);

			return -1;
		}

		umask (0);
	}

	/* Install signal handlers. */
	sigemptyset (&act.sa_mask);
	act.sa_flags = 0;
	act.sa_handler = SIG_IGN;
	sigaction (SIGPIPE, &act, NULL);

	act.sa_handler = stop;
	sigaction (SIGINT, &act, NULL);
	sigaction (SIGTERM, &act, NULL);

	act.sa_handler = handle_alarm;
	sigaction (SIGALRM, &act, NULL);

	act.sa_handler = handle_sigusr1;
	sigaction (SIGUSR1, &act, NULL);

	act.sa_handler = handle_sighup;
	sigaction (SIGHUP, &act, NULL);

	/* Set alarm every second. */
	interval.it_interval.tv_sec = 1;
	interval.it_interval.tv_usec = 0;
	interval.it_value.tv_sec = 1;
	interval.it_value.tv_usec = 0;
	if (setitimer (ITIMER_REAL, &interval, NULL) < 0) {
		perror ("setitimer");

		http_server_delete (&http_server);

		configuration_free (&mime_types);
		configuration_free (&conf);

		return -1;
	}

	ret = start_http_server (&http_server);

	http_server_delete (&http_server);

	dnscache_free (&dnscache);

	configuration_free (&conf);
	configuration_free (&mime_types);

	return ret;
}

void print_usage (const char *program)
{
	fprintf (stderr, "Usage: %s [--no-daemonize] [--conf <config_file>] [--mime <mime_types_file>]\n", program);
}

void stop (int nsignal)
{
	if (http_server.running) {
		if (http_server.log_fd != -1) {
			log_error ("Stopping Schaefchen.");
		}

		http_server.running = 0;
	}
}

void handle_alarm (int nsignal)
{
	http_server.handle_alarm = 1;
}

void handle_sigusr1 (int nsignal)
{
	http_server.handle_sigusr1 = 1;
}

void handle_sighup (int nsignal)
{
	http_server.handle_sighup = 1;
}
